package dyyx.miniioc;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class ApplicationContext implements BeanFactory {

	private final String packageName;

	private Map<String, Object> beans = new HashMap<String, Object>();

	private ApplicationContext(String packageName) {
		this.packageName = packageName;
	}

	private void refresh() throws Exception {
		Set<Class<?>> classes = ClassScanner.getClasses(packageName);
		for (Class<?> clazz : classes) {
			Bean bean = clazz.getAnnotation(Bean.class);
			if (bean != null) {
				String beanName = StrUtil.toLowerCaseFirstChar(clazz.getSimpleName());
				beans.put(beanName, clazz.getConstructor().newInstance());
			}
		}

		beans.forEach((beanName, bean) -> {

			Field[] fields = bean.getClass().getDeclaredFields();

			for (Field field : fields) {
				Inject inject = field.getAnnotation(Inject.class);
				if (inject == null) {
					continue;
				}
				String injectBeanName = inject.value();
				if (injectBeanName.length() == 0) {
					injectBeanName = field.getName();
				}
				Object _bean = beans.get(injectBeanName);
				if (_bean == null) {
					throw new RuntimeException("bean " + injectBeanName + " not found");
				}
				field.setAccessible(true);

				try {
				field.set(bean, _bean);
				}catch(Throwable e) {
					throw new RuntimeException("inject set error",e);
				}

			}

		});

	}

	public static ApplicationContext start(String packageName) throws Exception {
		ApplicationContext applicationContext = new ApplicationContext(packageName);
		applicationContext.refresh();
		return applicationContext;
	}

	@Override
	public <T> T getBean(Class<T> type) {
		for (Map.Entry<String, Object> stringObjectEntry : beans.entrySet()) {
			if (stringObjectEntry.getValue().getClass().equals(type)) {
				return (T) stringObjectEntry.getValue();
			}
		}
		return null;
	}

	@Override
	public Object getBean(String beanName) {
		return beans.get(beanName);
	}

	@Override
	public <T> T getBean(String beanName, Class<T> clazz) {
		return (T) getBean(beanName);
	}

}
